#include <atomic>
#include <cstdint>
#include <iostream>
#include <string.h>
#include <utility>
#include <memory>
#include <chrono>
#include <thread>
#include <boost/asio.hpp>

#include "../../log.h"

constexpr uint32_t MSG_MAX_LENGTH = 1024;
constexpr uint32_t MSG_LENGTH = sizeof(uint32_t);
constexpr uint32_t ID_LENGTH = sizeof(uint32_t);
constexpr uint32_t HEAD_LENGTH = MSG_LENGTH + ID_LENGTH; 

std::vector<std::thread> threads;

std::atomic_int g_num{0};

int main(void)
{
    using namespace boost::asio::ip;

    for(int i = 0; i < 100; ++i)
    {
        threads.emplace_back([i](){
            try
            {
                //创建上下文服务
                boost::asio::io_context ioc;
                //构造服务器endpoint
                tcp::endpoint ep(address::from_string("127.0.0.1"), 8080);
                //创建本地socket
                tcp::socket socket(ioc);
                //链接服务器
                boost::system::error_code error = boost::asio::error::host_not_found;
                socket.connect(ep, error);
                if(error)
                {
                    log_error("connect error, error code: {}, error message: {}", error.value(), error.message());
                    return;
                }
                log_debug("local ip: {}, local port: {}", socket.local_endpoint().address().to_string(), socket.local_endpoint().port());
                
                uint32_t id = 1001;
                int i = 0;
                while(i++ < 10)
                {
                    //发送数据
                    std::cout << "please input your message: ";
                    char request[MSG_MAX_LENGTH] = "hello world";
                    // std::cin.getline(request, MSG_MAX_LENGTH);
                    uint32_t len = strlen(request);
                    std::unique_ptr<char[]> send_buffer(new char[len + HEAD_LENGTH + 1]);
                    uint32_t len_network = boost::asio::detail::socket_ops::host_to_network_long(len);
                    uint32_t id_network = boost::asio::detail::socket_ops::host_to_network_long(id);
                    memcpy(send_buffer.get(), &id_network, ID_LENGTH);
                    memcpy(send_buffer.get() + ID_LENGTH, &len_network, MSG_LENGTH);
                    // log_debug("len: {}, id: {}", len, id);
                    memcpy(send_buffer.get() + HEAD_LENGTH, request, len);
                    send_buffer[len + HEAD_LENGTH] = '\0';
                    int ret = boost::asio::write(socket, boost::asio::buffer(send_buffer.get(), len + HEAD_LENGTH));
                    if(ret <= 0)
                    {
                        log_error("write error, ret: {}", ret);
                        return;
                    }


                    // char head_len[HEAD_LENGTH];
                    std::unique_ptr<char[]> head_len(new char[HEAD_LENGTH]);
                    boost::asio::read(socket, boost::asio::buffer(head_len.get(), HEAD_LENGTH));
                    uint32_t msg_id = 0;
                    uint32_t msg_len = 0;
                    memcpy(&msg_id, head_len.get(), ID_LENGTH);
                    memcpy(&msg_len, head_len.get() + ID_LENGTH, MSG_LENGTH);

                    msg_len = boost::asio::detail::socket_ops::network_to_host_long(msg_len);
                    // log_debug("msg_len: {}, id: {}", msg_len, id);
                    if(msg_len > MSG_MAX_LENGTH)
                    {
                        log_error("msg len is too long, msg_len: {}", msg_len);
                        return;
                    }
                    std::unique_ptr<char[]> p(new char[msg_len + 1]);
                    memset(p.get(), 0, msg_len);
                    ret = boost::asio::read(socket, boost::asio::buffer(p.get(), msg_len));
                    if(ret <= 0)
                    {
                        log_error("read error, ret: {}", ret);
                        return;
                    }
                    p[msg_len] = '\0';
                    log_info("reply is: {}", p.get());
                    ++g_num;
                }
            }
            catch (const std::exception& e)
            {
                log_error("Exception: ", e.what());
            }
        });
        std::this_thread::sleep_for(std::chrono::milliseconds(2));
    };

    for(auto& thread : threads)
    {
        thread.join();
    }

    int num = g_num.load();
    log_info("g_num: {}", num);
    return 0;
}